<!DOCTYPE html>
<script>
    /* call 函数 */
    Function.prototype.myCall = function (context) {
        var context = context || window
        // 给 context 添加一个属性
        // getValue.call(a, 'yck', '24') => a.fn = getValue
        context.fn = this
        // 将 context 后面的参数取出来
        var args = [...arguments].slice(1)
        // getValue.call(a, 'yck', '24') => a.fn('yck', '24')
        var result = context.fn(...args)
        // 删除 fn
        delete context.fn
        return result
    }

    /* bind 函数 */
    // 通过闭包调用 call || apply 可以实现 bind 函数
    // apply 数组参数； call() 参数用逗号分隔 == bind()()
    // 乞丐版
    Function.prototype.mybind = function (context, ...preArgs) {
        return (...args) => this.call(context, ...preArgs, ...args);
    };
    // 进阶版
    Function.prototype.mybind = function (context) {
        if (typeof this !== "function") {
            throw new TypeError("Error");
        }
        let _this = this;
        let arg = [...arguments].slice(1);
        return function F() {
            // 处理函数使用new的情况
            if (this instanceof F) {
                return new _this(...arg, ...arguments);
            } else {
                return _this.apply(context, arg.concat(...arguments));
            }
        };
    };

    /* apply 函数 */
    Function.prototype.myapply = function (context) {
        if (typeof this !== "function") {
            throw new TypeError("not funciton");
        }
        context = context || window;
        context.fn = this;
        let result;
        // 需要判断是否存储第二个参数
        // 如果存在，就将第二个参数展开
        if (arguments[1]) {
            result = context.fn(...arguments[1]);
        } else {
            result = context.fn();
        }

        delete context.fn;

        return result;
    };
</script>